/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#ifndef __QUATERNION_H__
#define __QUATERNION_H__

#pragma once

#include "Vector.h"

namespace math {

/** 
 *  A simple Quaternion class 
 */
class DLL_EXPORT Quaternion
{
public:

	/** Default constructor */
	Quaternion();

	/** Copy contructor */
	Quaternion( const Quaternion& copyFrom );

	/** Creates a Quaternion from specified values */
	Quaternion( float x, float y, float z, float w );

	inline float& operator[](unsigned int id)
	{
		assert(id <4);
		return (&x)[id];
	}

	/** Resets to a identity quaternion
	 * @return This quaternion
	 */
	Quaternion&	reset();

	/** Sets the quaternion to the provided values
	 * @param copyFrom another Quaternion to copy the values from.
	 */
	Quaternion&	set( const Quaternion& copyFrom );

	/** Sets the quaternion to the provided values
	 * @param x The x-value of the rotation axis
	 * @param y The y-value of the rotation axis
	 * @param z The z-value of the rotation axis
	 */
	Quaternion&	set( float x, float y, float z );

	/** Sets the quaternion to the provided values
	 * @param angle The angle of the rotation axis
	 * @param x The x-value of the rotation axis
	 * @param y The y-value of the rotation axis
	 * @param z The z-value of the rotation axis
	 */
	Quaternion& set( float x, float y, float z, float angle );

	/** Spherical linear interpolation. Interpolate from one Quaternion to the other Quaternion.
	 * @param p The starting Quaternion
	 * @param q The ending Quaternion
 	 * @param t The current time between interpolation. This is a float value between 0 and 1
 	 */
	void		slerp( const Quaternion &p, const Quaternion &q, float t );

	math::Matrix4 getMatrix4() const;

	math::Vector3 getVector() const;

	void        setValues( float val0, float val1, float val2, float val3 );

	/** Quaternion inverse */
	Quaternion  operator-() const;

	/** Quaternion assignment */
	Quaternion& operator=(const Quaternion &a);

	/** Quatenion addition */
	Quaternion  operator+(const Quaternion &a) const;

	/** Quatenion addition assignment */
	Quaternion& operator+=(const Quaternion &a);

	/** Quatenion subtraction */
	Quaternion  operator-(const Quaternion &a) const;

	/** Quatenion subtraction assignment */
	Quaternion& operator-=(const Quaternion &a);

	/** Quaternion multiplication */
	Quaternion  operator*(const Quaternion &a) const;

	/** Scalar multiplication */
	Quaternion  operator*(float a) const;

	/** Vector multiplication */
	//Vector3	    operator*(const Vector3 &v) const;

	/* Quaternion multiplication assignment */
	Quaternion& operator*=(const Quaternion &a);

	/** Scalar multiplication assignment */
	Quaternion& operator*=(float a);

	/** Equality */
	bool		operator==(const Quaternion &a) const;

	/** Inequality */
	bool		operator!=(const Quaternion &a) const;

	float		length() const;

	bool        compare(const Quaternion &a) const;

	Quaternion conj();

	/** Normalizes the values of the Quaternion. This is important since only normalized
	 * Quaternions are valid for rotations.
	 * @return This Quaternion
	 */
	Quaternion& normalize();

	// Rotate a vector using the quaternion
	math::Vector3 rotate(const math::Vector3 &vec);

	void print() const;


	/** The storage of the quaternion */
	float x,y,z,w;

	static const float      EPSILON;
	static const Quaternion ZERO;
	static const Quaternion IDENTITY;
};

inline Quaternion operator*(const float a, const Quaternion &quat)
{
	return quat*a;
}
/*
inline Vector3 operator*(const Vector3 &vec, const Quaternion &quat)
{
	return quat*vec;
}
*/

inline Quaternion operator*(Quaternion q, Vector3 v)
{
	return Quaternion( q.w*v.x + q.y*v.z - q.z*v.y,
					   q.w*v.y + q.z*v.z - q.x*v.z,
					   q.w*v.z + q.x*v.y - q.y*v.x,
					 -(q.x*v.x + q.y*v.y + q.z*v.z) );					
}

inline Quaternion operator*(Vector3 v, Quaternion q)
{
	return Quaternion( q.w*v.x + q.z*v.y - q.y*v.z,
					   q.w*v.y + q.x*v.z - q.z*v.x,
					   q.w*v.z + q.y*v.x - q.x*v.y,
					 -(q.x*v.x + q.y*v.y + q.z*v.z) );					
}



} // namespace

#endif
